package cn.iocoder.yudao.module.vos.service.secretaccount;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.module.vos.dal.dataobject.secretrechargerecord.SecretRechargeRecordDO;
import cn.iocoder.yudao.module.vos.dal.dataobject.tenantrequestlog.TenantRequestLogDO;
import cn.iocoder.yudao.module.vos.dal.mysql.secretrechargerecord.SecretRechargeRecordMapper;
import cn.iocoder.yudao.module.vos.dal.mysql.tenantrequestlog.TenantRequestLogMapper;
import cn.iocoder.yudao.module.vos.service.secretaccount.vo.ChargeResult;
import cn.iocoder.yudao.module.vos.service.tenantrequestlog.TenantRequestLogService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import cn.iocoder.yudao.module.vos.controller.admin.secretaccount.vo.*;
import cn.iocoder.yudao.module.vos.dal.dataobject.secretaccount.SecretAccountDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.vos.dal.mysql.secretaccount.SecretAccountMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList;
import static cn.iocoder.yudao.module.vos.enums.ErrorCodeConstants.*;

@Service
@Validated
@Slf4j
public class SecretAccountServiceImpl implements SecretAccountService {

    @Resource
    private SecretAccountMapper secretAccountMapper;

    @Resource
    private SecretRechargeRecordMapper secretRechargeRecordMapper;


    @Resource
    private TenantRequestLogService tenantRequestLogService;

    @Override
    public Long createSecretAccount(SecretAccountSaveReqVO createReqVO) {
        // 1) 逻辑层校验唯一
        validateAppSecretUnique(createReqVO.getAppSecret(), null);

        // 2) 插入（并发兜底：捕获唯一索引异常）
        SecretAccountDO secretAccount = BeanUtils.toBean(createReqVO, SecretAccountDO.class);
        secretAccountMapper.insert(secretAccount);
        return secretAccount.getId();
    }

    @Override
    public void updateSecretAccount(SecretAccountSaveReqVO updateReqVO) {
        // 0) 校验存在
        validateSecretAccountExists(updateReqVO.getId());

        // 1) 逻辑层校验唯一（排除自己）
        validateAppSecretUnique(updateReqVO.getAppSecret(), updateReqVO.getId());

        // 2) 更新（并发兜底：捕获唯一索引异常）
        SecretAccountDO updateObj = BeanUtils.toBean(updateReqVO, SecretAccountDO.class);
        secretAccountMapper.updateById(updateObj);
    }

    /** appSecret 全局唯一校验（excludeId 为 null 表示创建；否则为更新时排除本记录） */
    private void validateAppSecretUnique(String appSecret, Long excludeId) {
        Long cnt = secretAccountMapper.selectCount(
                new LambdaQueryWrapper<SecretAccountDO>()
                        .eq(SecretAccountDO::getAppSecret, appSecret)
                        .ne(excludeId != null, SecretAccountDO::getId, excludeId)
        );
        if (cnt != null && cnt > 0) {
            throw exception(APP_SECRET_DUPLICATE);
        }
    }

    @Override
    public void deleteSecretAccount(Long id) {
        // 校验存在
        validateSecretAccountExists(id);
        // 删除
        secretAccountMapper.deleteById(id);
    }

    @Override
    public void deleteSecretAccountListByIds(List<Long> ids) {
        // 删除
        secretAccountMapper.deleteByIds(ids);
    }

    private void validateSecretAccountExists(Long id) {
        if (secretAccountMapper.selectById(id) == null) {
            throw exception(SECRET_ACCOUNT_NOT_EXISTS);
        }
    }

    @Override
    public SecretAccountDO getSecretAccount(Long id) {
        return secretAccountMapper.selectById(id);
    }

    @Override
    public PageResult<SecretAccountDO> getSecretAccountPage(SecretAccountPageReqVO pageReqVO) {
        return secretAccountMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void recharge(SecretAccountRechargeReqVO reqVO) {
        // 1) 校验账户存在
        SecretAccountDO account = secretAccountMapper.selectById(reqVO.getId());
        if (account == null) {
            throw exception(SECRET_ACCOUNT_NOT_EXISTS);
        }

        // 2) 校验金额（此处非本次两个接口链路，暂不改为 ErrorCode）
        BigDecimal amount = reqVO.getRechargeAmount();
        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("充值金额必须大于 0");
        }
        amount = amount.setScale(2, RoundingMode.HALF_UP);

        // 3) 计算余额
        BigDecimal before = account.getBalance() == null ? BigDecimal.ZERO : account.getBalance();
        before = before.setScale(2, RoundingMode.HALF_UP);
        BigDecimal after = before.add(amount).setScale(2, RoundingMode.HALF_UP);

        // 4) 更新账户余额
        SecretAccountDO update = new SecretAccountDO();
        update.setId(account.getId());
        update.setBalance(after);
        secretAccountMapper.updateById(update);

        // 5) 写充值记录
        SecretRechargeRecordDO record = new SecretRechargeRecordDO();
        record.setRechargeTenantId(account.getRechargeTenantId());
        record.setSecretId(account.getId());
        record.setRechargeAmount(amount);
        record.setBeforeBalance(before);
        record.setAfterBalance(after);
        record.setRemark(reqVO.getRemark());
        secretRechargeRecordMapper.insert(record);
    }

    @Override
    public SecretAccountDO checkAndGetAccount(String appSecret, String requiredPermission) {
        // — Input normalization —
        String secret = StrUtil.blankToDefault(appSecret, "").trim();
        if (StrUtil.isBlank(secret)) {
            throw exception(APP_SECRET_HEADER_MISSING); // "Missing 'appSecret' in request header"
        }

        // — Account checks: existence / enabled / not expired —
        SecretAccountDO account = secretAccountMapper.selectByAppSecret(secret);
        if (account == null) {
            throw exception(SECRET_ACCOUNT_NOT_EXISTS);
        }
        if (Boolean.FALSE.equals(account.getEnabled())) {
            throw exception(ACCOUNT_DISABLED);
        }
        if (account.getExpireTime() != null && account.getExpireTime().isBefore(LocalDateTime.now())) {
            throw exception(ACCOUNT_EXPIRED);
        }

        // — Permission check (optional) —
        if (StrUtil.isNotBlank(requiredPermission)) {
            String perms = StrUtil.blankToDefault(account.getProductPermissions(), "");
            boolean ok = Arrays.stream(perms.split(","))
                    .map(String::trim)
                    .anyMatch(requiredPermission::equals);
            if (!ok) {
                throw exception(ACCOUNT_NOT_AUTHORIZED);
            }
        }

        // — Balance check: balance ≤ 0 —
        if (nvl(account.getBalance()).compareTo(BigDecimal.ZERO) <= 0) {
            throw exception(INSUFFICIENT_BALANCE);
        }
        return account;
    }

    // —— 私有工具：统一空值为 0，避免 NPE ——
    private static BigDecimal nvl(BigDecimal v) {
        return v == null ? BigDecimal.ZERO : v;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ChargeResult chargeAndLog(SecretAccountDO account,
                                     BigDecimal unitPrice,
                                     boolean shouldCharge,
                                     String url,
                                     String paramsMasked,
                                     String requestType,
                                     String respBrief,
                                     String requestNo,
                                     String ip) {
        BigDecimal price = nvl(unitPrice);
        BigDecimal chargedAmount = BigDecimal.ZERO;      // 本次实际扣费金额
        BigDecimal afterBalance  = nvl(account.getBalance());
        // —— 仅当需要扣费且单价>0 才进行 CAS 扣费 ——
        if (shouldCharge && price.compareTo(BigDecimal.ZERO) > 0) {
            int updated = secretAccountMapper.updateBalanceByCostWithCheck(account.getId(), price, price);
            if (updated <= 0) {
                throw exception(INSUFFICIENT_BALANCE);
            }
            chargedAmount = price;
            // 以 DB 为准刷新余额
            SecretAccountDO fresh = secretAccountMapper.selectById(account.getId());
            afterBalance = fresh != null && fresh.getBalance() != null
                    ? fresh.getBalance()
                    : afterBalance.subtract(price);
        }
        // —— 写请求日志（无论是否扣费都记一笔） ——
        try {
            // 这里“unitPrice”字段写入【实际扣费金额】：无结果则为 0
            insertRequestLog(account, url, paramsMasked, respBrief, requestType, chargedAmount, afterBalance, requestNo, ip);
        } catch (Exception e) {
            log.warn("写入请求日志失败 requestNo={}", requestNo, e);
        }

        return new ChargeResult(chargedAmount, afterBalance, chargedAmount.compareTo(BigDecimal.ZERO) > 0);
    }

    /** 写请求日志（submeter_tenant_request_log） */
    private void insertRequestLog(SecretAccountDO acc, String url, String params, String respBrief,
                                  String requestType, BigDecimal unitPrice, BigDecimal afterBalance,
                                  String requestNo, String ip) {
        TenantRequestLogDO logDO = new TenantRequestLogDO();
        logDO.setRechargeTenantId(acc.getRechargeTenantId());
        logDO.setUrl(url);
        logDO.setParams(params);
        logDO.setRespons(respBrief);
        logDO.setRequestType(requestType);
        logDO.setUnitPrice(unitPrice);
        logDO.setAfterBalance(afterBalance);
        logDO.setRequestNumber(requestNo);
        logDO.setRequestIp(ip);
        logDO.setSecretId(acc.getId());
        tenantRequestLogService.createTenantRequestLog(logDO);
    }

    /**
     * 根据 appSecret 获取租户（注意：此方法非本次两个接口链路，保留原语义）
     */
    public SecretAccountDO getByAppSecret(String appSecret) {
        SecretAccountDO secretAccount = secretAccountMapper.selectByAppSecret(appSecret);
        if (secretAccount == null) {
            throw new IllegalArgumentException("Invalid appSecret: " + appSecret);
        }
        return secretAccount;
    }
}
